#ifndef JFUNCTIONTRAITS
#define JFUNCTIONTRAITS

#include <functional>

namespace QTE
{

template <typename>
struct JMemberTraits
{
	using MemberType = void;
	using ClassType = void;
};

template <typename T, typename U>
struct JMemberTraits<U T::*>
{
	using MemberType = U;
	using ClassType = T;
};

template <typename C, typename R, typename... A>
struct JMemFuncType
{
	using type = typename std::conditional
	<
		std::is_const<C>::value,
		typename std::conditional<std::is_volatile<C>::value, R(C::*)(A...) const volatile, R(C::*)(A...) const>,
		typename std::conditional<std::is_volatile<C>::value, R(C::*)(A...) volatile, R(C::*)(A...)>
	>;
};

template <typename T>
struct __JFunctionTraits
{
	static constexpr bool isFunction = false;

	using ResultType = void;
	using FunctionType = void;

	template <typename>
	using MemberFunctionType = void;

	static constexpr std::size_t arity = 0;

	template <std::size_t> struct arg {
		using type = void;
	};
};

template <typename Return, typename... Args>
struct __JFunctionTraits<Return(Args...)>
{
	static constexpr bool isFunction = true;

	using ResultType = Return;
	using FunctionType = Return(Args...);

	template <typename Owner>
	using MemberFunctionType =
	typename JMemFuncType<typename std::remove_pointer<typename std::remove_reference<Owner>>, Return, Args...>::type;

	static constexpr std::size_t arity = sizeof...(Args);

	template <std::size_t i> struct arg {
		using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
	};
};

template <typename Return, typename... Args>
struct __JFunctionTraits<Return(*)(Args...)> : public __JFunctionTraits<Return(Args...)> {};

template <typename Class, typename Return, typename... Args>
struct __JFunctionTraits<Return(Class::*)(Args...)> : public __JFunctionTraits<Return(Args...)> {
	using OwnerType = Class&;
};

template <typename Class, typename Return, typename... Args>
struct __JFunctionTraits<Return(Class::*)(Args...) const> : public __JFunctionTraits<Return(Args...)> {
	using OwnerType = const Class&;
};

template <typename Class, typename Return, typename... Args>
struct __JFunctionTraits<Return(Class::*)(Args...) volatile> : public __JFunctionTraits<Return(Args...)> {
	using OwnerType = volatile Class&;
};

template <typename Class, typename Return, typename... Args>
struct __JFunctionTraits<Return(Class::*)(Args...) const volatile> : public __JFunctionTraits<Return(Args...)> {
	using OwnerType = const volatile Class&;
};

template <typename Function>
struct __JFunctionTraits<std::function<Function>> : public __JFunctionTraits<Function> {};

template <typename Result, typename...OriArgs, typename...RelArgs>
struct __JFunctionTraits<Result(*(OriArgs...))(RelArgs...)> : public __JFunctionTraits<Result(RelArgs...)> {};

template <template <typename _Function> class Bind, typename Function>
struct __JFunctionTraits<Bind<Function>> : public __JFunctionTraits<Function> {};

template <typename T>
struct __JFunctionTraits<T&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<const T&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<volatile T&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<const volatile T&> : public __JFunctionTraits<T> {};

template <typename T>
struct __JFunctionTraits<T&&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<const T&&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<volatile T&&> : public __JFunctionTraits<T> {};
template <typename T>
struct __JFunctionTraits<const volatile T&&> : public __JFunctionTraits<T> {};

namespace JFTConcept
{

template <typename T>
__JFunctionTraits<T> __require(...);

template <typename T>
__JFunctionTraits<decltype(&T::operator())> __require(decltype(&T::operator())*);

} //namespace JFTConcept

template <typename T>
struct JFunctionTraits : decltype(JFTConcept::__require<T>(nullptr)) {};

} //namespace QTE


#endif //JFUNCTIONTRAITS
